<template>
  <el-form
    ref="form"
    v-if="model"
    :validate-on-rule-change="false"
    :model="model"
    :rules="rules"
    v-bind="$attrs"
  >
    <template v-for="(item, index) in options" :key="index">
      <el-form-item
        v-if="!item.children || !item.children!.length"
        :prop="item.prop"
        :label="item.label"
      >
        <component
          v-if="item.type !== 'upload' && item.type !== 'editor'"
          :placeholder="item.placeholder"
          v-model="model[item.prop!]"
          :is="`el-${item.type}`"
          v-bind="item.attrs"
        />
        <el-upload
          v-if="item.type === 'upload'"
          v-bind="item.uploadAttrs"
          :on-preview="onPreview"
          :on-remove="onRemove"
          :on-success="onSuccess"
          :on-error="onError"
          :on-progress="onProgress"
          :on-change="onChange"
          :on-exceed="onExceed"
          :before-upload="beforeUpload"
          :before-remove="beforeRemove"
        >
          <slot name="uploadArea"></slot>
          <slot name="uploadTip"></slot>
        </el-upload>
        <div id="editor" v-if="item.type === 'editor'"></div>
      </el-form-item>
      <el-form-item
        v-if="item.children && item.children.length"
        :prop="item.prop"
        :label="item.label"
      >
        <component
          :placeholder="item.placeholder"
          v-bind="item.attrs"
          :is="`el-${item.type}`"
          v-model="model[item.prop!]"
        >
          <component
            v-for="(child, i) in item.children"
            :key="i"
            :is="`el-${child.type}`"
            :label="child.label"
            :value="child.value"
          >
          </component>
        </component>
      </el-form-item>
    </template>
    <el-form-item>
      <slot name="action" :form="form" :model="model"></slot>
    </el-form-item>
  </el-form>
</template>

<script lang="ts" setup>
import { PropType, ref, onMounted, watch, nextTick } from "vue";
import { FormInstance, FormOptions } from "./types/types";
import cloneDeep from "lodash/cloneDeep"; // 打包只会打包cloneDeep
import E from "wangeditor";

import {
  UploadFile,
  UploadFiles,
  UploadProgressEvent,
  UploadRawFile,
  UploadUserFile,
} from "element-plus";

let props = defineProps({
  options: {
    type: Array as PropType<FormOptions[]>,
    required: true,
  },
  httpRequest: {
    type: Function,
  },
});
let model = ref<any>(null);
let rules = ref<any>(null);
let form = ref<FormInstance | null>();
let edit = ref();

let initForm = () => {
  if (props.options && props.options.length) {
    let m: any = {};
    let r: any = {};
    // model里只有属性名和值的键值对，rules里只有属性名和rule的键值对
    props.options.map((item: FormOptions) => {
      m[item.prop!] = item.value;
      r[item.prop!] = item.rules;
      if (item.type === "editor") {
        // 用到dom操作，那就要nexttick
        nextTick(() => {
          if (document.getElementById("editor")) {
            const editor = new E("#editor");
            editor.config.placeholder = item.placeholder!;
            editor.create();
            editor.txt.html(item.value);
            editor.config.onchange = function (newHtml: string) {
              model.value[item.prop!] = newHtml;
            };
            edit.value = editor;
          }
        });
      }
    });
    model.value = cloneDeep(m);
    rules.value = cloneDeep(r);
  }
};

// 重置表单的方法
let resetFields = () => {
  // 重置element-plus的表单
  form.value!.resetFields();
  // 重置富文本编缉器的内容
  if (props.options && props.options.length) {
    let editorItem = props.options.find((item) => item.type === "editor");
    if (editorItem) {
      edit.value.txt.html(editorItem.value);
    }
  }
};

let validate = () => {
  return form.value?.validate;
}

// 如果不使用方法，直接defineExpose,只能获得初始值
let getFormData = () => {
  return model.value;
}

// 分发方法
defineExpose({
  resetFields,
  validate,
  getFormData
})

onMounted(() => {
  initForm();
});

// 监听父组件传递进来的props
watch(
  () => props.options,
  () => {
    initForm();
  },
  { deep: true }
);

let emits = defineEmits([
  "on-preview",
  "on-remove",
  "on-success",
  "on-error",
  "on-progress",
  "on-change",
  "on-exceed",
  "before-upload",
  "before-remove",
]);

// 上传组件的所有方法
let onPreview = (uploadFile: UploadFile) => {
  emits("on-preview", uploadFile);
};
let onRemove = (uploadFile: UploadFile, uploadFiles: UploadFiles) => {
  emits("on-remove", uploadFile, uploadFiles);
};
let onSuccess = (
  response: any,
  uploadFile: UploadFile,
  uploadFiles: UploadFiles
) => {
  // 上传图片成功 给表单上传项赋值
  let uploadItem = props.options.find((item) => item.type === "upload")!;
  model.value[uploadItem.prop!] = { response, uploadFile, uploadFiles };
  emits("on-success", response, uploadFile, uploadFiles);
};
let onError = (
  error: Error,
  uploadFile: UploadFile,
  uploadFiles: UploadFiles
) => {
  emits("on-error", error, uploadFile, uploadFiles);
};
let onProgress = (
  evt: UploadProgressEvent,
  uploadFile: UploadFile,
  uploadFiles: UploadFiles
) => {
  emits("on-progress", evt, uploadFile, uploadFiles);
};
let onChange = (uploadFile: UploadFile, uploadFiles: UploadFiles) => {
  emits("on-change", uploadFile, uploadFiles);
};
let onExceed = (files: File[], uploadFiles: UploadUserFile[]) => {
  emits("on-exceed", files, uploadFiles);
};
let beforeUpload = (rawFile: UploadRawFile) => {
  emits("before-upload", rawFile);
};
let beforeRemove = (uploadFile: UploadFile, uploadFiles: UploadFiles) => {
  emits("before-remove", uploadFile, uploadFiles);
};
</script>

<style lang="scss" scoped></style>
